package com.kexio.common.excel;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.beans.PropertyDescriptor;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.*;

public final class ExcelImportUtils {
    private ExcelImportUtils() {}

    public static List<Map<String, String>> readAsMap(InputStream in) {
        try (Workbook wb = new XSSFWorkbook(in)) {
            Sheet sheet = wb.getSheetAt(0);
            if (sheet == null) return Collections.emptyList();
            Iterator<Row> it = sheet.rowIterator();
            if (!it.hasNext()) return Collections.emptyList();
            Row header = it.next();
            List<String> heads = new ArrayList<>();
            for (Cell cell : header) heads.add(getCellString(cell));
            List<Map<String, String>> rows = new ArrayList<>();
            while (it.hasNext()) {
                Row r = it.next();
                Map<String, String> m = new LinkedHashMap<>();
                for (int i = 0; i < heads.size(); i++) {
                    Cell c = r.getCell(i);
                    m.put(heads.get(i), getCellString(c));
                }
                rows.add(m);
            }
            return rows;
        } catch (Exception e) {
            throw new RuntimeException("Excel import failed: " + e.getMessage(), e);
        }
    }

    public static <T> List<T> readAsBeans(InputStream in, Class<T> beanClass, Map<String, String> headerToField) {
        List<Map<String, String>> rows = readAsMap(in);
        List<T> list = new ArrayList<>();
        for (Map<String, String> row : rows) {
            list.add(mapToBean(row, beanClass, headerToField));
        }
        return list;
    }

    private static <T> T mapToBean(Map<String, String> row, Class<T> beanClass, Map<String, String> headerToField) {
        try {
            T bean = beanClass.getDeclaredConstructor().newInstance();
            for (Map.Entry<String, String> e : row.entrySet()) {
                String header = e.getKey();
                String field = headerToField != null ? headerToField.getOrDefault(header, header) : header;
                setProperty(bean, field, e.getValue());
            }
            return bean;
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    private static void setProperty(Object bean, String field, String value) {
        try {
            PropertyDescriptor pd = new PropertyDescriptor(field, bean.getClass());
            Method setter = pd.getWriteMethod();
            if (setter == null) return;
            Class<?> type = pd.getPropertyType();
            Object v = convert(type, value);
            setter.invoke(bean, v);
        } catch (Exception ignored) {}
    }

    private static Object convert(Class<?> type, String value) {
        if (value == null) return null;
        String v = value.trim();
        if (v.isEmpty()) return null;
        try {
            if (type == String.class) return v;
            if (type == Integer.class || type == int.class) return Integer.parseInt(v);
            if (type == Long.class || type == long.class) return Long.parseLong(v);
            if (type == Double.class || type == double.class) return Double.parseDouble(v);
            if (type == Float.class || type == float.class) return Float.parseFloat(v);
            if (type == Boolean.class || type == boolean.class) return Boolean.parseBoolean(v);
        } catch (Exception ignored) {}
        return v;
    }

    private static String getCellString(Cell c) {
        if (c == null) return "";
        DataFormatter dataFormatter = new DataFormatter();
        return dataFormatter.formatCellValue(c);
    }
}


